package social.server.main;

import java.util.ArrayList;
import java.util.GregorianCalendar;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import javax.naming.InvalidNameException;

import social.server.Exceptions.AlreadyUsedLoginException;
import social.server.Exceptions.CantFindUserException;
import social.server.Exceptions.InvalidCategoryException;
import social.server.Exceptions.InvalidIdSessionException;
import social.server.Exceptions.InvalidLoginException;
import social.server.Exceptions.InvalidNameAtributeException;
import social.server.Exceptions.NoIdSessionFoundException;
import social.server.Exceptions.NoItemFoundException;
import social.server.Exceptions.NoUserFoundException;
import social.server.historicActivity.FriendAdditionActivity;
import social.server.historicActivity.HistoricActivity;
import social.server.historicActivity.PublicateRequestActivity;
import social.server.ranking.RankedUser;
import social.server.ranking.Ranking;

/**
 * Classe responsavel por administrar e manter a lista contendo os usuarios. Eh
 * um Singleton, para acessar a unica instancia da classe deve-se usar o metodo
 * getInstance().
 * 
 * 
 */
public class System {

	private GregorianCalendar systemCalendar = new GregorianCalendar();
	private static List<User> userList = new ArrayList<User>();
	private static List<Integer> requestCodeList = new ArrayList<Integer>();

	public static String[] userAtributes = { "nome", "endereco" };
	public static String[] itemAtributes = { "nome", "categoria", "descricao" };
	public static String[] itemSortingTypes = { "crescente", "decrescente" };
	public static String[] itemSortingCriterion = { "dataCriacao", "reputacao" };
	public static String[] rankingCategory = { "global", "amigos" };

	private static final Ranking ranking = new Ranking();
	private static final System system = new System();

	public GregorianCalendar getSystemCalendar() {
		return systemCalendar;

	}

	public void setSystemCalendar(GregorianCalendar systemCalendar) {
		this.systemCalendar = systemCalendar;
	}

	/**
	 * Retorna a unica instancia da classe.
	 * 
	 * @return Um objeto System.
	 */
	public static System getInstance() {
		return system;
	}

	/**
	 * Retorna a unica instancia da classe.
	 * 
	 * @return Um objeto MessageManager.
	 */
	public static MessageManager getMessageManagerInstance() {
		return MessageManager.getInstance();
	}

	/**
	 * Acessa o objeto ranking e retorna o ranking dos usuarios.
	 * 
	 * @return Uma lista de Strings representando o ranking dos usuarios.
	 */
	public List<RankedUser> getGlobalRanking() {
		return ranking.getGlobalRanking();
	}

	/**
	 * Acessa o objeto ranking e retorna o ranking referente aos amigos de um
	 * usuario especifico.
	 * 
	 * @param user
	 *            Usuario referecia para o ranking.
	 * @return Um array de Strings contendo os amigos do usuario orndenados, com
	 *         base em suas reputacoes.
	 * @throws Exception
	 *             Pode lancar excecao ao usar os metodos getUserReputation(),
	 *             da classe System.
	 */
	public List<RankedUser> getFriendsRanking(User user) throws Exception {
		return ranking.getFriendsRanking(user);
	}

	/**
	 * Incrementa a reputacao de um usuario.
	 * 
	 * @param userLogin
	 *            Login do usuario que se deseja incrementar a reputacao.
	 */
	public void increaseUserReputation(String userLogin) {
		ranking.increaseUserReputation(userLogin);
	}

	/**
	 * Retorna a lista de requisicoes.
	 * 
	 * @return A lista de requisicoes
	 */
	public List<Integer> getRequestCodeList() {
		return requestCodeList;
	}

	/**
	 * Adiciona uma requisicao a lista.
	 * 
	 * @param num
	 *            Parametro a ser adicionado.
	 */
	public void addRequestCodeList(int num) {
		requestCodeList.add(num);
	}

	/**
	 * Retorna a lista de usuarios do sistema.
	 * 
	 * @return A lista de usuarios do sistema.
	 */
	public List<User> getUserList() {
		return userList;
	}

	/**
	 * Adiciona um usuario a lista de usuarios.
	 * 
	 * @param user
	 *            Usuario a ser adicionado.
	 * @throws Exception
	 *             Lanca excecao caso ja exista um usuario com o login dado.
	 */
	public void addUser(User user) throws Exception {
		for (int i = 0; i < userList.size(); i++) {
			if (userList.get(i).getLogin().equals(user.getLogin())) {
				throw new AlreadyUsedLoginException();
			}
		}
		userList.add(user);
		ranking.addRankedUser(user.getLogin());
	}

	/**
	 * Remove um usuario da lista de usuarios.
	 * 
	 * @param user
	 *            Usuario a ser removido.
	 */
	public void removeUser(User user) {
		userList.remove(user);
	}

	/**
	 * Retorna um usuario tomando como parametro de busca seu login.
	 * 
	 * @param login
	 *            Login do usuario a ser buscado.
	 * @return Usuario referente ao login dado como parametro.
	 * @throws Exception
	 *             Lanca excecao caso o Usuario nao seja encontrado ou caso o
	 *             login seja invalido.
	 */
	public User getUser(String login) throws Exception {
		java.util.Iterator<User> it = userList.iterator();
		if (login == null || login.equals(""))
			throw new InvalidLoginException();
		while (it.hasNext()) {
			User user = it.next();
			if (user.getLogin().equalsIgnoreCase(login)) {
				return user;
			}
		}
		throw new NoUserFoundException();
	}

	/**
	 * Retorna um usuario tomando como parametro de busca seu id de sessao.
	 * 
	 * @param idSession
	 *            Id sessao do usuario a ser buscado.
	 * @return Usuario referente a id sessao dada como parametro.
	 * @throws Exception
	 *             Lanca excecao caso a id sessao nao seja encontrada ou caso a
	 *             id sessao seja invalido.
	 */
	public User getUserById(String idSession) throws Exception {
		if (idSession == null || idSession.isEmpty())
			throw new InvalidIdSessionException();

		java.util.Iterator<User> it = userList.iterator();
		while (it.hasNext()) {
			User user = it.next();
			if (user.getIdSession().equals(idSession)) {
				return user;
			}
		}
		throw new NoIdSessionFoundException();
	}

	/**
	 * Autentica um usuario tomando como parametro o login do dado usuario (Um
	 * usuario eh autenticado quando ele esta cadastrado no sistema).
	 * 
	 * @param login
	 *            Login do usuario a ser autenticado.
	 * @return O resultado da operacao.
	 */
	public boolean authenticateUser(String login) {
		java.util.Iterator<User> it = userList.iterator();

		while (it.hasNext()) {
			User user = it.next();
			if (user.getLogin().equals(login)) {
				return true;
			}
		}
		return false;
	}

	/**
	 * Procura a ocorrencia de uma chave em uma dada cadeia de strings.
	 * 
	 * @param key
	 *            Chave a qual se procura uma ocorrencia.
	 * @param searched
	 *            Cadeia de string na qual esta sendo objeto da procura.
	 * @return False caso alguma das palavras sejam vazias ou caso nao exista
	 *         ocorrencias da chave da cadeia de strings, e true caso exista ao
	 *         menos uma ocorrencia.
	 */
	public boolean compareKeys(String key, String searched) {
		if (key.isEmpty() || searched.isEmpty()) {
			return false;
		}
		String[] keyArray = key.toLowerCase().split(" ");
		String[] searchedArray = searched.toLowerCase().split(" ");

		for (int i = 0; i < keyArray.length; i++) {
			for (int j = 0; j < searchedArray.length; j++) {
				if (searchedArray[j].contains(keyArray[i])) {
					return true;
				}
			}
		}
		return false;
	}

	/**
	 * Metodo usado para procurar usuarios, tomando como chaves da busca uma
	 * palavra chave (palavra essa que deve estar contida no dado atributo
	 * referente ao usuario), atributo no qual se baseia a busca.
	 * 
	 * @param idSession
	 *            Id sessao do usuario que esta realizando a busca.
	 * @param keyWord
	 *            Palavra chave na qual se baseia a busca.
	 * @param atribute
	 *            Atributo no qual se baseia a busca.
	 * @return Uma lista de usuarios que se encaixam na busca.
	 * @throws Exception
	 *             Lanca excecao Caso nao se possa achar um usuarios com os
	 *             dados parametros.
	 */
	public List<String> searchUser(String idSession, String keyWord,
			String atribute) throws Exception {
		Validator.validateIdSection(idSession);
		Validator.validateKeyWord(keyWord);
		Validator.validateUserAtribute(atribute);

		List<String> searchedUserList = new ArrayList<String>();

		if (atribute.equalsIgnoreCase("Nome")) {
			searchUserByName(searchedUserList, keyWord, idSession);
		} else if (atribute.equalsIgnoreCase("Endereco")) {
			searchUserByAddress(searchedUserList, keyWord, idSession);
		}
		if (searchedUserList.isEmpty()) {
			throw new CantFindUserException();
		}
		return searchedUserList;
	}

	/**
	 * Metodo auxilar ao metodo "searchUser" que procurar usuarios tomando como
	 * chave o nome.
	 * 
	 * @param searchedUserList
	 *            Lista onde estarao os usuarios apos a busca.
	 * @param keyWord
	 *            Chave da busca.
	 * @param idSession
	 *            Id sessao do usuario que requisitou a busca.
	 * @throws Exception
	 *             Lanca excecao ao ultilizar outros metodos.
	 */
	private void searchUserByName(List<String> searchedUserList,
			String keyWord, String idSession) throws Exception {
		java.util.Iterator<User> it = userList.iterator();
		User user;
		User thisUser = getUser(idSession);

		while (it.hasNext()) {
			user = it.next();
			if (compareKeys(keyWord, user.getName())) {
				if (!thisUser.equals(user)) {
					searchedUserList.add(user.getLogin());
				}
			}
		}
	}

	/**
	 * Metodo auxilar ao metodo "searchUser" que procurar usuarios tomando como
	 * chave o endereco.
	 * 
	 * @param searchedUserList
	 *            Lista onde estarao os usuarios apos a busca.
	 * @param keyWord
	 *            Chave da busca.
	 * @param idSession
	 *            Id sessao do usuario que requisitou a busca.
	 * @throws Exception
	 *             Lanca excecao ao ultilizar outros metodos.
	 */
	public void searchUserByAddress(List<String> searchedUserList,
			String keyWord, String idSession) throws Exception {
		java.util.Iterator<User> it = userList.iterator();
		User user;
		User thisUser = getUser(idSession);

		while (it.hasNext()) {
			user = it.next();
			if (compareKeys(keyWord, user.getNewAddress())) {
				if (!thisUser.equals(user)) {
					searchedUserList.add(user.getLogin());
				}
			}
		}
	}

	public List<String> searchUser(String idSession)
			throws InvalidIdSessionException, NoIdSessionFoundException {
		Validator.validateIdSection(idSession);
		// TODO implementar, (metodo da US20)
		return null;
	}

	/**
	 * Metodo usado para procurar itens, tomando como chaves da busca um chave
	 * (chave essa que deve estar contida no nome do item), atributo no qual se
	 * baseia a busca, tipo de ordenacao (ordem no qual se deseja ordenar o
	 * resultado), criterio de ordenacao (criterio no qual se deseja ordenar a o
	 * resultado).
	 * 
	 * @param idSection
	 *            Id sessao do usuario que realiza a busca.
	 * @param key
	 *            Chave na qual se baseia a busca.
	 * @param atribute
	 *            Atributo no qual se baseia a busca.
	 * @param sortingType
	 *            Tipo de ordenacao na qual se deseja ordenar o resultado.
	 * @param sortingCriterion
	 *            Criterio de ordenacao no qual se deseja ordenar o resultado.
	 * @return Uma lista de itens que se encaixam na busca.
	 * @throws Exception
	 *             Lanca excecao ao usar outros metodos.
	 */
	public List<Item> searchItem(String idSection, String key, String atribute,
			String sortingType, String sortingCriterion) throws Exception {
		Validator.validateIdSection(idSection);
		Validator.validateKey(key);
		Validator.validateItemAtribute(atribute);
		Validator.validateItemSortingType(sortingType);
		Validator.validateItemSortingCriterion(sortingCriterion);

		List<Item> searchedItemList = new ArrayList<Item>();

		User user = getUserById(idSection);

		if (atribute.equalsIgnoreCase("nome")) {
			searcheItemByName(user.getFriendList(), key, searchedItemList);
		} else if (atribute.equalsIgnoreCase("categoria")) {
			searcheItemByCategory(user.getFriendList(), key, searchedItemList);
		} else if (atribute.equalsIgnoreCase("descricao")) {
			searcheItemByDescription(user.getFriendList(), key,
					searchedItemList);
		}
		if (searchedItemList.isEmpty()) {
			throw new NoItemFoundException();
		}
		if (sortingCriterion.equalsIgnoreCase("dataCriacao")) {
			searchedItemList = sortByDateCriterion(searchedItemList,
					sortingType);
		} else if (sortingCriterion.equalsIgnoreCase("reputacao")) {
			searchedItemList = sortByReputationCriterion(searchedItemList,
					sortingType);
		}
		return searchedItemList;
	}

	/**
	 * Metodo responsavel por auxiliar o metodo "searchItem", este metodo faz
	 * uma busca nos amigos do dado usuario, em busca de um item tomando como
	 * chave da busca o nome do item.
	 * 
	 * @param user
	 *            Usuario requisitante da busca.
	 * @param key
	 *            Chave da busca.
	 * @param searchedItemList
	 *            Lista com o resultado da busca.
	 * @throws Exception
	 *             Lanca excecao ao usar outros metodos.
	 */
	public void searcheItemByName(List<String> friendList, String key,
			List<Item> searchedItemList) throws Exception {

		String friend;
		List<Item> friendSearchedItemList;
		java.util.Iterator<String> it = friendList.iterator();

		while (it.hasNext()) {
			friend = it.next();
			friendSearchedItemList = getUser(friend).getItemByName(key);
			if (!friendSearchedItemList.isEmpty()) {
				Iterator<Item> friendIt = friendSearchedItemList.iterator();
				while (friendIt.hasNext()) {
					Item item = friendIt.next();
					searchedItemList.add(item);
				}
			}
		}
	}

	/**
	 * Metodo responsavel por auxiliar o metodo "searchItem", este metodo faz
	 * uma busca nos amigos do dado usuario, em busca de um item tomando como
	 * chave da busca a cetegoria do item.
	 * 
	 * @param user
	 *            Usuario requisitante da busca.
	 * @param key
	 *            Chave da busca.
	 * @param searchedItemList
	 *            Lista com o resultado da busca.
	 * @throws Exception
	 *             Lanca excecao ao usar outros metodos.
	 */
	public void searcheItemByCategory(List<String> friendList, String key,
			List<Item> searchedItemList) throws Exception {

		String friend;
		List<Item> friendSearchedItemList;
		java.util.Iterator<String> it = friendList.iterator();

		while (it.hasNext()) {
			friend = it.next();
			friendSearchedItemList = getUser(friend).getItemByCategory(key);
			if (!friendSearchedItemList.isEmpty()) {
				Iterator<Item> friendIt = friendSearchedItemList.iterator();
				while (friendIt.hasNext()) {
					Item item = friendIt.next();
					searchedItemList.add(item);
				}
			}
		}
	}

	/**
	 * Metodo responsavel por auxiliar o metodo "searchItem", este metodo faz
	 * uma busca nos amigos do dado usuario, em busca de um item tomando como
	 * chave da busca a descricao do item.
	 * 
	 * @param user
	 *            Usuario requisitante da busca.
	 * @param key
	 *            Chave da busca.
	 * @param searchedItemList
	 *            Lista com o resultado da busca.
	 * @throws Exception
	 *             Lanca excecao ao usar outros metodos.
	 */
	public void searcheItemByDescription(List<String> friendList, String key,
			List<Item> searchedItemList) throws Exception {

		String friend;
		List<Item> friendSearchedItemList;
		java.util.Iterator<String> it = friendList.iterator();

		while (it.hasNext()) {
			friend = it.next();
			friendSearchedItemList = getUser(friend).getItemByDescription(key);
			if (!friendSearchedItemList.isEmpty()) {
				Iterator<Item> friendIt = friendSearchedItemList.iterator();
				while (friendIt.hasNext()) {
					Item item = friendIt.next();
					searchedItemList.add(item);
				}
			}
		}
	}

	/**
	 * Ordena uma lista de itens com base na reputacao e um dado Tipo de
	 * Ordenacao. Usado como suporte pela busca de itens [searchItem()].
	 * 
	 * @param searchedItemList
	 *            Lista de itens a ser ordenada.
	 * @param sortingType
	 *            Tipo de ordenacao usado.
	 * @return A lista de itens ordenada.
	 * @throws Exception
	 *             Lanca excecao ao usar outros metodos.
	 */
	private List<Item> sortByReputationCriterion(List<Item> searchedItemList,
			String sortingType) throws Exception {
		LinkedList<Item> tempList = new LinkedList<Item>();
		if (sortingType.equalsIgnoreCase("crescente")) {
			increasingReputationSort(searchedItemList, tempList);
		} else if (sortingType.equalsIgnoreCase("decrescente")) {
			decreasingReputationSort(searchedItemList, tempList);
		}
		return tempList;
	}

	/**
	 * Ordena uma lista de itens com base na data de criacao do item e um dado
	 * Tipo de Ordenacao. Usado como suporte pela busca de itens [searchItem()].
	 * 
	 * @param searchedItemList
	 *            Lista de itens a ser ordenada.
	 * @param sortingType
	 *            Tipo de ordenacao usado.
	 * @return A lista de itens ordenada.
	 * @throws Exception
	 *             Lanca excecao ao usar outros metodos.
	 */
	public List<Item> sortByDateCriterion(List<Item> searchedItemList,
			String sortingType) throws Exception {
		LinkedList<Item> tempList = new LinkedList<Item>();
		if (sortingType.equalsIgnoreCase("crescente")) {
			increasingDateSort(searchedItemList, tempList);
		} else if (sortingType.equalsIgnoreCase("decrescente")) {
			decreasingDateSort(searchedItemList, tempList);
		}
		return tempList;
	}

	/**
	 * Ordena uma lista de atividades historicas com base na data de criacao de
	 * cada atividade.
	 * 
	 * @param relationshipHistoricList
	 *            Lista de atividades historicas a ser ordenada.
	 * @return A lista de atividades historicas ordenada.
	 */
	public LinkedList<HistoricActivity> sortRelationshipHistoricList(
			List<HistoricActivity> relationshipHistoricList) {
		LinkedList<HistoricActivity> finalList = new LinkedList<HistoricActivity>();

		HistoricActivity minimum;

		Iterator<HistoricActivity> it;

		while (relationshipHistoricList.size() > 0) {
			minimum = relationshipHistoricList.get(0);
			it = relationshipHistoricList.iterator();
			while (it.hasNext()) {
				HistoricActivity activity = it.next();
				if (activity.compareDates(minimum.getDate())) {
					minimum = activity;
				}
			}
			relationshipHistoricList.remove(minimum);
			finalList.add(minimum);
		}
		return finalList;
	}

	/**
	 * Metodo auxiliar ao metodo "SortByDateCriterion" que ordena uma lista de
	 * itens em ordem decrescente tomando como chave a data de criacao do item.
	 * 
	 * @param searchedItemList
	 *            Lista de itens a ser ordenada.
	 * @param tempList
	 *            Lista com o resultado final.
	 * @throws Exception
	 *             Lanca excecao ao usar outros metodos.
	 */
	public void decreasingDateSort(List<Item> searchedItemList,
			LinkedList<Item> tempList) throws Exception {

		Iterator<Item> it;
		Item max;

		while (searchedItemList.size() > 0) {
			max = searchedItemList.get(0);
			it = searchedItemList.iterator();
			while (it.hasNext()) {
				Item item = it.next();
				if (max.compareDates(item.getDate())) {					
					max = item;
				}
			}
			searchedItemList.remove(max);
			tempList.add(max);
		}
	}

	/**
	 * Metodo auxiliar ao metodo "SortByDateCriterion" que ordena uma lista de
	 * itens em ordem crescente tomando como chave a data de criacao do item.
	 * 
	 * @param searchedItemList
	 *            Lista de itens a ser ordenada.
	 * @param tempList
	 *            Lista com o resultado final.
	 * @throws Exception
	 *             Lanca excecao ao usar outros metodos.
	 */
	public void increasingDateSort(List<Item> searchedItemList,
			LinkedList<Item> tempList) throws Exception {

		Iterator<Item> it;
		Item max;

		while (searchedItemList.size() > 0) {
			max = searchedItemList.get(0);
			it = searchedItemList.iterator();
			while (it.hasNext()) {
				Item item = it.next();
				if (item.compareDates(max.getDate())) {
					max = item;
				}
			}
			searchedItemList.remove(max);
			tempList.addFirst(max);
		}
	}

	/**
	 * Metodo auxiliar ao metodo "SortByReputationCriterion" que ordena uma
	 * lista de itens em ordem decrescente tomando como chave a reputacao do
	 * dono do item.
	 * 
	 * @param searchedItemList
	 *            Lista de itens a ser ordenada.
	 * @param tempList
	 *            Lista com o resultado final.
	 * @throws Exception
	 *             Lanca excecao ao usar outros metodos.
	 */
	public void decreasingReputationSort(List<Item> searchedItemList,
			LinkedList<Item> tempList) throws Exception {

		Iterator<Item> it;
		Item max;

		while (searchedItemList.size() > 0) {
			max = searchedItemList.get(0);
			it = searchedItemList.iterator();
			while (it.hasNext()) {
				Item item = it.next();
				if (max.compareReputations(item)) {
					max = item;
				}
			}
			searchedItemList.remove(max);
			tempList.add(max);
		}
	}

	/**
	 * Metodo auxiliar ao metodo "SortByReputationCriterion" que ordena uma
	 * lista de itens em ordem crescente tomando como chave a reputacao do dono
	 * do item.
	 * 
	 * @param searchedItemList
	 *            Lista de itens a ser ordenada.
	 * @param tempList
	 *            Lista com o resultado final.
	 * @throws Exception
	 *             Lanca excecao ao usar outros metodos.
	 * @param searchedItemList
	 * @param tempList
	 * @throws Exception
	 */
	public void increasingReputationSort(List<Item> searchedItemList,
			LinkedList<Item> tempList) throws Exception {

		Iterator<Item> it;
		Item max;

		while (searchedItemList.size() > 0) {
			max = searchedItemList.get(0);
			it = searchedItemList.iterator();
			while (it.hasNext()) {
				Item item = it.next();
				if (item.compareReputations(max)) {
					max = item;
				}
			}
			searchedItemList.remove(max);
			tempList.addFirst(max);
		}
	}

	/**
	 * Limpa a lista de atividades historicas removendo duplicacoes de
	 * atividades (FriendAdditionActivity).
	 * 
	 * @param relationshipHistoricList
	 *            Lista de atividades historicas a ser limpada.
	 * @param userName
	 *            nome do usuario que esta executando a acao.
	 * @return A lista de atividades historicas limpa.
	 */
	public List<HistoricActivity> cleanRelationshipHistoricListFriendAddition(
			List<HistoricActivity> relationshipHistoricList, String userName) {
		LinkedList<HistoricActivity> finalList = new LinkedList<HistoricActivity>();
		Iterator<HistoricActivity> it = relationshipHistoricList.iterator();

		// Adiciona os elementos que sao do tipo"FriendAdditionActivity" a lista
		// final.
		while (it.hasNext()) {
			HistoricActivity activity = it.next();
			if (FriendAdditionActivity.class.isInstance(activity)) {
				finalList.add(activity);
			}
		}

		// Laco para remover duplicidades.
		for (int j = 0; j < finalList.size(); j++) {
			HistoricActivity activity = finalList.get(j);
			if (((FriendAdditionActivity) activity).getUserName().equals(
					userName)) {
				for (int i = 0; i < finalList.size(); i++) {
					HistoricActivity targetActivity = finalList.get(i);
					if (activity.equals(targetActivity)) {
						finalList.remove(i);
						i--;
					}
				}
			}
		}
		it = relationshipHistoricList.iterator();
		// Adiciona os elementos restantes (que nao sao do tipo
		// "FriendAdditionActivity" a lista final.
		while (it.hasNext()) {
			HistoricActivity activity = it.next();
			if (!FriendAdditionActivity.class.isInstance(activity)) {
				finalList.add(activity);
			}
		}
		return finalList;
	}

	/**
	 * Limpa a lista de atividades historicas removendo duplicacoes de
	 * atividades (PublicateRequestActivity).
	 * 
	 * @param relationshipHistoricList
	 * @return Lista sem duplicacoes (PublicateRequestActivity).
	 */

	public List<HistoricActivity> cleanRelationshipHistoricListPublicationActivity(
			List<HistoricActivity> relationshipHistoricList) {
		int cont = 0;

		// For convencional para evitar ConcurrentModificationException
		for (int i = 0; i < relationshipHistoricList.size(); i++) {
			cont = 0;
			for (int j = 0; j < relationshipHistoricList.size(); j++) {
				if ((relationshipHistoricList.get(i) instanceof PublicateRequestActivity)
						&& (relationshipHistoricList.get(j) instanceof PublicateRequestActivity)) {
					if (relationshipHistoricList.get(i).equals(
							relationshipHistoricList.get(j))) {
						++cont;
						if (cont > 1) {
							relationshipHistoricList.remove(j);
						}
					}

				}

			}
		}
		return relationshipHistoricList;

	}

	/**
	 * Retorna a reputacao de um usuario tomando como parametro o login do
	 * usuario em questao.
	 * 
	 * @param userLogin
	 *            Login do usuario em questao.
	 * @return A reputacao do usuario.
	 * @throws Exception
	 *             Lanca excecao ao usar outros metodos.
	 */
	public int getUserReputation(String userLogin) throws Exception {
		return getUser(userLogin).getReputation();
	}

	/**
	 * Cria um objeto item e retorna o objeto criado.
	 * 
	 * @return o objeto item criado.
	 * @throws InvalidCategoryException
	 *             Caso a categoria seja invalida.
	 * @throws InvalidNameException
	 * @throws InvalidNameAtributeException
	 *             Caso o nome do item seja invalido
	 */
	public Item createItem(String ownerLogin, String name, String description,
			String category) throws InvalidCategoryException,
			InvalidNameAtributeException {
		Validator.validateItemName(name);
		Validator.validateItemCategory(category);
		return new Item(ownerLogin, name, description, category);
	}

	/**
	 * Retorna o usuario que esta se querendo visualizar, e adiciona ao usuario
	 * alvo uma visita recente.
	 * 
	 * @param login
	 *            Targer user login.
	 * @return Target User.
	 * @throws Exception
	 * 
	 */
	public User visualizeUser(String userName, String targetUserLogin)
			throws Exception {
		getUser(targetUserLogin).addLastVisit(userName);
		return getUser(targetUserLogin);
	}

	/**
	 * Limpa a lista de usuarios e a lista de requisicoes.
	 */
	public void clearSystem() {
		userList.clear();
		requestCodeList.clear();
		ranking.clear();
	}

	/**
	 * Encerra o sistema.
	 */
	public void terminateSystem() {
	}
}
